
local jass  = require 'jass.common'
local hook  = require 'jass.hook'
local storm = require 'jass.storm'
local message = require 'jass.message'
local runtime = require 'jass.runtime'

local string = string
local math   = math
local table  = table

local table_remove = table.remove
local table_insert = table.insert
local table_concat = table.concat


class = {}
function class:__newindex(name,value)
    if not rawget(value, (('create'))) then 
        --create = new(nil,...)
        value.create = function (...)
            local ui = value.new(nil,...)
            if package.local_test then 
                local info = debug.getinfo(4, "Sl") or {}
                local bool = info.short_src:find('core')
                --- 不是底层创建的控件记录路径用来热更新
                if not bool then 
                    ui._src = info.short_src
                    ui._line = info.currentline
                end 
            end 
            return ui
        end
    end
    

    if not rawget(value,(('add_child'))) then 
        --add_child = new(...)
        value.add_child = function (...)
            local ui = value.new(...)
            
            if package.local_test then 
                local info = debug.getinfo(4, "Sl") or {}
                local bool = info.short_src:find('core')
                --- 不是底层创建的控件记录路径用来热更新
                if self.normal_image=='cs' then
                    print('==========',bool)
                    for k,v in pairs(info) do
                        print(k,v)
                    end
                end
                if not bool then 
                    ui._src = info.short_src
                    ui._line = info.currentline
                end 
            end 
            return ui
        end
    end

    if not rawget(value,(('get_instance'))) then 
        value.get_instance = function ()
            local instance = value.instance 
            if instance == nil then 
                instance = value.create()
                value.instance = instance
            end 
            return instance
        end 
    end 

    rawset(self,name,value)
    
    if ac.frame and not ac.frame['add_' .. name] then 
        -- add_<class> = parent:add(class,...)
        ac.frame['add_' .. name]  = function (parent,...)
            return parent:add(value,...)
        end
    end 
    
end

setmetatable(class,class)

local frame = {}
ac.frame = frame
--给所有类注册模板方法 

--- 句柄管理器
frame.handle_manager = handle_manager_class:create()

--- 存放所有控件的表
frame.allframes = {}

--- 控件计数器
frame.dbgcount = 0

---所有的模板
frame.toc = {}

---已加载的FDF
frame.loaded = {}

-- 1 鼠标点击
-- 2 鼠标进入
-- 3 鼠标离开
-- 6 鼠标滚轮
-- 7 复选框打钩
-- 8 取消打钩
-- 9 改变文本

---魔兽自带的控件消息事件
function FrameEventCallBack(handle,event_id)
    if ac.ignore_flag == true then return end 
    local control = frame.allframes[handle]
    if control ~= nil and control.removed ~= true then 
        local types = control.type
        local class = class[types] or frame
        if class.events and class.events[event_id] then 
            xpcall(class.events[event_id],runtime.error_handle,control)
        end
    end
end

---保存一个模板
function frame:load(name,fdf)
    if frame.toc[name] then 
        print("模板已经被使用",name) 
        return 
    end
    for check in fdf:gmatch [[Frame%s*%"[%w_]+%"%s%"([%w_]+)%"]] do 
        if frame.loaded[check] then 
            return log.error('FDF内的控件定义名称重复',check)
        else
            frame.loaded[check] = true
        end
    end
    frame.toc[name] = {
        index = 0,
        fdf = fdf,
    }
end

--- 根据保存过的模板生成一个新的模板 返回索引
function frame:compose(name)
    --- 拿到加载的模板
    local temp = frame.toc[name]
    if not temp then return 0 end 
    local fdf = temp.fdf
    local index = temp.index + 1
    for key in temp.fdf:gmatch [[Frame%s*%"[%w_]+%"%s(%"[%w_]+%")]] do 
        fdf = fdf:gsub(key,([["%s(%d)"]]):format(key:match [[%"(.+)%"]],index))
    end
    load_fdf(fdf)
    temp.index = index
    return index
end

--世界坐标转屏幕坐标
function frame.w2s(x,y,z)
    local screen_x, screen_y, scale = message.world_to_screen(x, y, z)
    if  screen_x and screen_y then 
        return screen_x * 1920 / 0.8, screen_y * 1080 / 0.6, scale
    end 
end

--屏幕坐标转世界坐标
function frame.s2w(x,y)
    local screen_x, screen_y = x / 1920 * 0.8, y / 1080 * 0.6
    return message.screen_to_world(screen_x, screen_y)
end

--转换坐标为屏幕坐标
function frame.ScreenPosition(x, y)
    x = x / 1920 * 0.8
    y = (1080 - y) / 1080 * 0.6
    return x,y
end

--转换宽高为屏幕宽高
function frame.ScreenSize(width, height)
    width = width / 1920 * 0.8
    height = height / 1080  * 0.6
    return width,height
end 

setmetatable(frame,frame)





local birth_index = 0
local mt = {}

frame.__index = mt

--结构
mt.type = 'frame'

mt.allframes = frame.allframes

---句柄
mt.handle = nil 

---唯一整数标识
mt._index = nil

---唯一字符串标识
mt._name = '基类'

---层级
mt.level = 1

---显示状态
mt.show_flag = true

---simple类控件
mt.simple_flag = false

---x坐标
mt.x = 0

---y坐标
mt.y = 0

---宽度
mt.w = 0

---高度
mt.h = 0

--透明度
mt.alpha = 1.0

---渲染限制
mt.view_port = false

---屏蔽控件相应
mt.ignore_flag = false

---ESC弹起时自动隐藏,该控件必须是绝对父级
mt.auto_hide = false

---启用状态
mt.enable_flag = true

---父级控件
mt.parent = setmetatable({
    handle = japi.GetGameUI(),
    children = { },
    simple_flag = true,
    hide_has_event = true,
    w = 1920,
    h = 1080,
},{__index = mt})


function mt:__tostring()
    return string.format('[%s]->%d',self._name,self.handle or 0)
end

--- 转换控件为对象
function mt.convert(handle, name, parent, is_default)
    local control_class = class[name] or mt
    if handle == nil or handle == 0 or control_class==nil then 
        return 
    end 
    if frame.allframes[handle] then 
        return frame.allframes[handle]
    end 
    local parent = parent or frame.allframes[japi.FrameGetParent(handle)] or mt.parent
    local control = ac.frame.create(parent, nil, 0, 0, 32, 32)
    
    --设置元方法
    for name, meta_method in pairs(control_class) do 
        if name:sub(1, 2) == '__' then 
            control[name] = meta_method
        end 
    end 

    --实例对象绑定控件类
    control.__index = control_class
    control.default = is_default
    control.handle = handle
    

    control.parent = parent
    table.insert(parent.children, control)

    frame.allframes[handle] = control
    frame.dbgcount = frame.dbgcount + 1
    return control
end

--创建对象
function mt.create(parent, types, x, y, width, height)
    local index = frame.handle_manager:allocate()
    birth_index = birth_index + 1
    local ui = {
        x = x,
        y = y,
        w = width,
        h = height,
        children = {},
        _index = index,
        _birth_index = birth_index,
        _name = (parent and parent._name or 'object_') .. tostring(index) .. '_',
    }
    setmetatable(ui,ui)
    return ui
end


--构造器
function mt.builder(control_class,param)
    local control = ac.frame.create(param.parent, param.fdf or control_class.fdf, 0, 0, 32, 32)
    control.type = control_class.fdf
    
    --设置元方法
    for name, meta_method in pairs(control_class) do 
        if name:sub(1, 2) == '__' then 
            control[name] = meta_method
        end 
    end 

    --实例对象绑定控件类
    control.__index = control_class
    for name, value in pairs(param) do 
        control[name] = value
    end 
    
    local parent = param.parent
    if parent then
        if param.w == nil then  control.w = parent.w  end
        if param.h == nil then  control.h = parent.h  end
    end

    if package.local_test then 
        local info = debug.getinfo(4, "Sl") or {}
        local bool = info.short_src:find('core')
        --- 不是底层创建的控件记录路径用来热更新
        if not bool then 
            control._src = info.short_src
            control._line = info.currentline
        end
    end 
    local object = control:init()

    if parent then 
        table.insert(parent.children, object)
    end 

    local sort = {}
    for name, value in pairs(param) do
        if type(value) == 'table' and not getmetatable(value) and value.type then
            table.insert(sort,{name, value,})
        end
    end
    --将子控件按层级排序来设置显示顺序
    table.sort( sort , function( a , b )
        return ( a[2].level or 1 ) < ( b[2].level or 1 )
    end)

    for _, data in ipairs(sort) do
        local name  = data[1]
        local value = data[2]
        local clild_class = rawget(class, value.type)
        if clild_class ~= nil then 
            value.parent = control
            control[name] = clild_class:builder(value)
        end
    end

    frame.dbgcount = frame.dbgcount + 1
    return object
end


---删除控件
function mt:remove()
    if self.removed then return end

    if self:is_simple() then 
        return 
    end

    local parent    = self.parent
    local controls  = parent.children

    self:unbind_world()
    
    self.removed = true

    if controls then
        for i = #controls, 1, -1 do
            local control = controls[i]
            if control == self then
                table.remove(controls,i)
                break
            end
        end
    end

    --销毁子控件集
    local children = self.children

    --先解除引用
    self.children = nil

    for i = #children, 1, -1 do
        local child = children[i]
        if child then
            child:remove()
        end
    end

    local index = self._index
    frame.handle_manager:free(index)
    self._index = nil

    frame.allframes[self.handle] = nil
    japi.DestroyFrame(self.handle)
    
    self.handle = 0

    frame.dbgcount = frame.dbgcount - 1
end
 
function mt.add(self,class,...)
    return class.add_child(self,...)
end

function mt:child_builder(param)
    local class = class[param.type or ''] or self:get_this_class()
    if class then 
        param.parent = self 
        return class:builder(param)
    end 
end

---获取父级
function mt:get_parent()
    return self.parent
end

---是simple控件
function mt:is_simple()
    return self.simple_flag
end 

---是转换来的
function mt:is_convert()
    return self.convert_flag 
end

---按钮是恒定的
function mt:is_default()
    return self.default
end 

---判断是否启用
function mt:is_enable()
    return self.enable_flag
end

---设置启用状态
function mt:set_enable(bool)
    self.enable_flag = bool
    japi.FrameSetEnable(self.handle,self.enable_flag)
end 

--是否绝对显示
function mt:is_absolute_show()
    local bool = self:is_show()
    if bool == true then 
        local control = self.parent 
        while (bool == true) do 
            if control == ac.frame.parent or control:is_simple() then 
                break 
            end 
            bool = control:is_show()
            control = control.parent
        end 
    end
    return bool 
end 

---是否可见
function mt:is_show()
    if self.removed then 
        return false 
    end
    return self.show_flag
end

---显示
function mt:show()
    if self.removed then return end
    if self.show_flag then return end
    self.show_flag = true
    japi.FrameShow(self.handle,true)
end

---隐藏
function mt:hide()
    if self.removed then return end
    if self.show_flag ~= true then return end
    self.show_flag = false
    japi.FrameShow(self.handle,false)
end

---获取中心位置
function mt:get_center()
    return self.x + self.w/2,self.y + self.h/2
end

---获取实际中心位置
function mt:get_real_center()
    local x,y = self:get_real_position()
    return x + self.w/2,y + self.h/2
end

---获取位置
function mt:get_position()
    return self.x,self.y
end

---设置位置
function mt:set_position(x,y)
    if self.removed then return end
    self.x = x
    self.y = y 
    if self.parent~=mt.parent then 
        x = x - (self.parent.scroll_x or 0)
        y = y - (self.parent.scroll_y or 0)
    end 
    if (self:get_parent() == mt.parent) or self:is_simple() then
        japi.FrameSetAbsolutePoint(self.handle,0,x / 1920 * 0.8,(1080 - y) / 1080 * 0.6)
    else
        japi.FrameSetPoint(self.handle,0,self.parent.handle,0,x / 1920 * 0.8,-y / 1080 * 0.6)
    end
end


---设置实际坐标位置
function mt:set_real_position(x, y)
    local rx, ry = self:get_real_position()
    x = x - (rx - self.x)
    y = y - (ry - self.y)
    self:set_position(x, y)
end

---获取宽
function mt:get_width()
    if self:is_simple() then
        return self.w
    end
    return japi.FrameGetWidth(self.handle) / 0.8 * 1920
end

---获取高
function mt:get_height()
    if self:is_simple() then
        return self.h
    end
    return japi.FrameGetHeight(self.handle) / 0.6 * 1080
end

---设置宽度
function mt:set_width(width)
    if self.removed then return end 
    self.w = width
    japi.FrameSetWidth(self.handle, width / 1920 * 0.8)
    return self
end

---设置高度
function mt:set_height(height)
    if self.removed then return end 
    self.h = height
    japi.FrameSetHeight(self.handle, height / 1080 * 0.6)
    return self
end

--设置透明度
function mt:set_alpha(alpha)
    if self.removed then return end 
    if alpha <= 1 then
        alpha = alpha * 0xff
    end
    japi.FrameSetAlpha(self.handle,alpha)
end

--获取透明度
function mt:get_alpha()
    return japi.FrameGetAlpha(self.handle)
end

--设置层级
function mt:set_level(level)
    if self.removed then return end 
    japi.FrameSetLevel(self.handle, level)
end 

---获取控件大小
function mt:get_control_size()
    return self:get_width(),self:get_height()
end

---设置控件大小
function mt:set_control_size(width,height)
    if self.removed then return end 
    self.w = width
    self.h = height
    width = width / 1920 * 0.8
    height = height / 1080  * 0.6
    japi.FrameSetSize(self.handle,width,height)
end

 ---屏蔽控件事件
function mt:set_ignore_trackevents(bool)
    self.ignore_flag = bool
    japi.FrameSetIgnoreTrackEvents(self.handle, bool)
end

---设置UI渲染限制(超出父级将不会渲染)
function mt:set_view_port(bool)
    self.view_port = bool
    japi.FrameSetViewPort(self.handle, bool)
end

---控件包含坐标
function mt:has_point(x,y)
    local ox,oy = self:get_real_position()
    if x >= ox and
        y >= oy and
        x <= ox + self.w and
        y <= oy + self.h
    then
        return true
    end
    return false
end

function mt:remove_tooltip()
end

---控件包含鼠标坐标
function mt:has_mouse()
    return self:has_point(ac.hardware:get_mouse_pos())
end

local x,y = 100,100
local w,h = 200,200
local old = 1.2
x,y = 80,80
w,h = 240,240
local size = 1
local rea = 1/1.2


---设置缩放
function mt:set_relative_size(size,flag)
    local scale = self._scale or 1
    local default = self.default_size or 1
    local old_size = (self.relative_size or 1) * default
    local real_size = 1 / old_size * size * scale
    self.relative_size = size
    self.default_size = scale 
    if self.type:find('model') then 
        self:set_size(size)
    elseif self.type == 'text' then 
        self:set_size(size)
    end 
    if flag then
        local x = self.x - (self.w*(real_size-1)) / 2
        local y = self.y - (self.h*(real_size-1)) / 2
        self:set_position(x ,y)
    end
    self:set_control_size(self.w * real_size, self.h * real_size)
    for _, child in ipairs(self.children) do
        child._scale = scale
        child:set_relative_size(size)
        child:set_position(child.x * real_size,child.y * real_size)
    end
end

---设置缩放 w
function mt:set_relative_w(size)
    local scale = self._scale_w or 1
    local default = self.default_size_w or 1
    local old_size = (self.relative_size_w or 1) * default
    local real_size = 1 / old_size * size * scale
    self.relative_size_w = size
    self.default_size_w = scale 
    self:set_control_size(self.w * real_size, self.h)
    if self.type:find('model') then 
        local scales = table.copy(self.scales)
        scales[1] = 1/scales[1]
        scales[2] = 1/scales[2]
        self:set_scales(scales)
        self.scales[1] = self.scales[1] * real_size
        self:set_scales(self.scales)
    end
    for _, child in ipairs(self.children) do
        child._scale_w = scale
        child:set_relative_w(size)
        child:set_position(child.x * real_size,child.y)
    end
end

function mt:set_relative_h(size)
    local scale = self._scale_h or 1
    local default = self.default_size_h or 1
    local old_size = (self.relative_size_h or 1) * default
    local real_size = 1 / old_size * size * scale
    self.relative_size_h = size
    self.default_size_h = scale 
    self:set_control_size(self.w, self.h * real_size)
    if self.type:find('model') then 
        local scales = table.copy(self.scales)
        scales[1] = 1/scales[1]
        scales[2] = 1/scales[2]
        self:set_scales(scales)
        self.scales[2] = self.scales[2] * real_size
        self:set_scales(self.scales)
    end
    for _, child in ipairs(self.children) do
        child._scale_h = scale
        child:set_relative_h(size)
        child:set_position(child.x,child.y * real_size)
    end
end

function mt:set_scroll_x(is_depth)
    local function find(object)
        local x = 0 
        if object.children then 
            for name,control in ipairs(object.children) do 
                if control.handle 
                and (control.x + control.w) > x 
                and control.type~='slider'
                and control.show_flag then 
                    x = control.x + control.w

                    if is_depth then 
                        x = x + find(control)
                    end
                end 
            end 
        end 
        return x 
    end
    
    return find(self)
end

function mt:get_child_max_y(is_depth)
    local function find(object)
        local y = 0 
        if object.children then 
            for name,control in ipairs(object.children) do 
                if control.handle 
                and (control.y + control.h) > y 
                and control.type~='slider'
                and control.show_flag then 
                    y = control.y + control.h

                    if is_depth then 
                        y = y + find(control)
                    end
                end 
            end 
        end 
        return y 
    end
    
    return find(self)
end


function mt:set_scroll_y(y)
    local max_y = self:get_child_max_y()
    
    if y == 0 then 
    elseif y < 0 then 
        y = 0
    elseif y + self.h > max_y then 
        y = max_y - self.h
    end
    self.scroll_y = y + (self.ex_scroll or 0)
    --滚动的时候刷新UI所有子控件位置
    for name,control in ipairs(self.children) do 
        if control.type~='slider' then  
            control:set_position(control.x,control.y)
        end 
    end 
    
end

function mt:set_scroll_x(x)
    local max_x = self:get_child_max_x()
    if x == 0 then 
    elseif x + self.w > max_x then 
        x = max_x - self.w
    elseif x < 0 then 
        x = 0
    end     

    self.scroll_x = x
    --滚动的时候刷新UI所有子控件位置
    for name,control in ipairs(self.children) do 
        if control.type~='slider' then  
            control:set_position(control.x,control.y)
        end 
    end 
    
end

---派发事件
function mt:notify(name, ...)
    local object = self
    if not object.hide_has_event then
        while object ~= nil and not object:is_simple() do
            if not object:is_show() then
                return 
            end
            local parent = object.parent
            if parent.hide_has_event then
                break
            end
            object = parent
        end
    end
    self:event(name, ...)
end

---派发事件
function mt:dispatch(name, ...)
    local object = self
    if not object.hide_has_event then
        while object ~= nil and not object:is_simple() do
            if not object:is_show() then
                return false
            end
            local parent = object.parent
            if parent.hide_has_event then
                break
            end
            object = parent
        end
    end
    return self:event(name, ...)
end

---事件回调
function mt:event(name, ...)
    local retval = true
    local func = self[name]
    if func then
        retval = func(self,...)
    end 
    if self.message_stop == true then --停止消息对父控件的转发
        return retval
    end
    if retval == nil then
        retval = true
    end
    --将消息转发到父控件里
    local object = self.parent
    while (object ~= nil) and not object:is_simple() and retval ~= false do
        local event = object[name]
        if event ~= nil then
            retval = event(object, self, ...)
        end
        object = object.parent
    end 
    return retval
end
